package org.softee.functional;

import java.util.Arrays;
import java.util.Collection;

import org.junit.Test;

public class GenericsTypeInferenceProblem {

    @Test
    public void testTypeInference() {
        Collection<Integer> ints = Arrays.asList(-1, 2, 3);

        {
            /*
             * Compiler error (on choose):
             * The method choose(Collection<Comparable<Comparable<T>>>) in the type
             * GenericsTypeInferenceProblem.Choice<Comparable<Comparable<T>>>
             * is not applicable for the arguments (Collection<Integer>)
             */
            // Why does it refer to "<Comparable<Comparable<T>>"?

            // Integer first = first().choose(ints);
        }

        {
            /* Compiler error (on cast):
             * Cannot cast from GenericsTypeInferenceProblem.Choice<Comparable<Comparable<T>>>
             * to GenericsTypeInferenceProblem.Choice<Integer>
             */

            // Why does it refer to "<Comparable<Comparable<T>>>"?

            // Integer first = ((Choice<Integer>)first()).choose(ints);
        }

        {
            // Workaround 1 (temporary variable)
            Choice<Integer> firstChoice = first();
            Integer first = firstChoice.choose(ints);
        }


        {
            // Workaround 2: (Explicit cast)

            // This looks like an error, but it compiles and works!
            Integer first = GenericsTypeInferenceProblem.<Integer>first().choose(ints);
        }

        {
            // Workaround 3: (special method with explicit return type)

            Integer first = firstInteger().choose(ints);
        }

    }


    public static Choice<Integer> firstInteger() {
        return first();
    }

    public static <T extends Comparable<T>> Choice<T> first() {
        return new Choice<T>() {
            @Override
            public T choose(Collection<T> values) {
                return values.iterator().next();
            }
        };
    }

    public static interface Choice<T> {
        T choose(Collection<T> values);
    }
}
